using System;
using System.Drawing;
using Microsoft.DirectX;
using RPGOnline.Controls.Map.Layers.Lighting;

namespace RPGOnline.Controls.Map.Layers
{
    public class ObstructionEdge
    {
        public Vector2 StartPoint { get; private set; }
        public Vector2 EndPoint { get; private set; }

        public ObstructionEdge(Point startPoint, Point endPoint)
        {
            StartPoint = new Vector2(startPoint.X, startPoint.Y);
            EndPoint = new Vector2(endPoint.X, endPoint.Y);
        }

        public ObstructionEdge(Map map, int x, int y, ObstructionSide obstructionSide)
        {
            StartPoint = CalculateStartPoint(map, x, y, obstructionSide);
            EndPoint = CalculateEndPoint(map, x, y, obstructionSide);
        }

        private Vector2 CalculateEndPoint(Map map, int x, int y, ObstructionSide obstructionSide)
        {
            switch (obstructionSide)
            {
                case ObstructionSide.Left:
                    y++;
                    break;
                case ObstructionSide.Right:
                    x++;
                    break;
                case ObstructionSide.Bottom:
                    x++;
                    y++;
                    break;
                case ObstructionSide.Top:
                    break;
                default:
                    throw new ArgumentOutOfRangeException("obstructionSide");
            }

            return new Vector2(
                map.TileSize.Width * x, 
                map.TileSize.Height * y
            );
        }

        private Vector2 CalculateStartPoint(Map map, int x, int y, ObstructionSide obstructionSide)
        {
            switch (obstructionSide)
            {
                case ObstructionSide.Left:
                    break;
                case ObstructionSide.Right:
                    x++;
                    y++;
                    break;
                case ObstructionSide.Bottom:
                    y++;
                    break;
                case ObstructionSide.Top:
                    x++;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("obstructionSide");
            }

            return new Vector2(
                map.TileSize.Width * x,
                map.TileSize.Height * y
            );
        }

        public bool IsFacingLight(Light light)
        {
            Vector2 normal = CalculateNormal();
            Vector2 lightToStart = StartPoint - light.Position;

            return Vector2.Dot(normal, lightToStart) > 0;
        }

        public Vector2 CalculateNormal()
        {
            Vector2 startToEnd = EndPoint - StartPoint;
            return new Vector2(startToEnd.Y, -startToEnd.X);
        }

        public ShadowPolygon CreateShadowPolygon(Light light)
        {
            ShadowPolygon shadowPolygon = new ShadowPolygon();
            shadowPolygon.AddPoint(ProjectPoint(StartPoint, light));
            shadowPolygon.AddPoint(StartPoint);
            shadowPolygon.AddPoint(ProjectPoint(EndPoint, light));
            shadowPolygon.AddPoint(EndPoint);

            return shadowPolygon;
        }

        public ShadowPolygon CreateDebugShadowPolygon(Light light)
        {
            ShadowPolygon shadowPolygon = new ShadowPolygon();
            shadowPolygon.AddPoint(StartPoint);
            shadowPolygon.AddPoint(ProjectPoint(StartPoint, light));
            shadowPolygon.AddPoint(ProjectPoint(EndPoint, light));
            shadowPolygon.AddPoint(EndPoint);
            shadowPolygon.AddPoint(StartPoint);

            return shadowPolygon;
        }

        private Vector2 ProjectPoint(Vector2 point, Light light)
        {
            Vector2 lightToPoint = point - light.Position;
            float extraLengthNeededToReachRadius = light.Radius - lightToPoint.Length();
            Vector2 vectorToAdd = lightToPoint;
            vectorToAdd.Normalize();
            vectorToAdd.Multiply(extraLengthNeededToReachRadius);
            Vector2 projectedPoint = point;
            projectedPoint.Add(vectorToAdd);
            return projectedPoint;
        }
    }
}